﻿Imports Microsoft.Scripting
Imports Microsoft.Scripting.Hosting
Imports Microsoft.Scripting.Runtime
Imports Microsoft.Scripting.Runtime.ScriptDomainManager

Imports CintieFramework.Packaging


Public Class Script

    Public Event ScriptReady(ByVal script As Script)

    Private _References As Dictionary(Of String, String)
    Private _Code As String
    Private _BaseFile As IO.FileInfo
    Private _Language As Languages
    Private _OldCode As String
    Private _ScriptInfo As CintieFramework.ScriptInformation
    Private _InstanceID As String

    Private PackageFile As IO.FileInfo

    Public ScriptEngine As Microsoft.Scripting.Hosting.ScriptEngine
    Public Scope As Microsoft.Scripting.Hosting.ScriptScope


    Public Enum Languages
        IronPython
        IronRuby
        CintieScript
    End Enum

    Public ReadOnly Property References() As Dictionary(Of String, String)
        Get
            Return _References
        End Get

    End Property

    Public Property Code() As String
        Get
            Return _Code
        End Get
        Set(ByVal value As String)
            _Code = value
        End Set
    End Property

    Public Property Language() As Languages
        Get
            Return _Language
        End Get
        Set(ByVal value As Languages)
            _Language = value
        End Set
    End Property

    Public ReadOnly Property ScriptInformation As CintieFramework.ScriptInformation
        Get
            Return _ScriptInfo
        End Get
    End Property
    ''' <summary>
    ''' Execute the current script and get an object containing the result of this execution
    ''' </summary>
    ''' <param name="Context"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Public Function Execute(ByVal Context As ScriptContext)

        Try
            'Set the variables of the current context
            For Each item In Context.Vars
                Scope.SetVariable(item.Key, item.Value)
            Next

            'Add the references of that this script will include
            For Each item In _References
                Scope.SetVariable(item.Key, item.Value)
            Next

            'Create the scope
            Dim Source As Microsoft.Scripting.Hosting.ScriptSource = _
                ScriptEngine.CreateScriptSourceFromString(Code, _
                                                          Microsoft.Scripting.SourceCodeKind.Statements)

            'And execute the script
            Source.Execute(Scope)
        Catch ex As Exception
            Return (ex.Message)
        End Try

        'We get the main Function variable, this will be returned as the result
        'If the variable not exist then this will return "Variable removed"
        If Not (Scope.ContainsVariable(Context.MainFunction)) Then
            Return ("Variable removed")
        Else
            'Return the requested variable
            Return Scope.GetVariable(Context.MainFunction)
        End If

    End Function

    Public Function Execute(Of T)(ByVal Context As ScriptContext) As T
        'Try
        'Set the variables of the current context
        For Each item In Context.Vars
            Scope.SetVariable(item.Key, item.Value)
        Next

        'Add the references of that this script will include
        For Each item In _References
            Scope.SetVariable(item.Key, item.Value)
        Next

        'Create the scope
        Dim Source As Microsoft.Scripting.Hosting.ScriptSource = _
            ScriptEngine.CreateScriptSourceFromString(Code, _
                                                      Microsoft.Scripting.SourceCodeKind.Statements)

        'And execute the script

        Try
            Source.Execute(Scope)
        Catch ex As Exception
            Console.WriteLine(ex.Message)
        End Try



        'We get the main Function variable, this will be returned as the result
        'If the variable not exist then this will return "Variable removed"

        Try
            If Not (Scope.ContainsVariable(Context.MainFunction)) Then
                'Throw New LostVariable(String.Format("Can't find variable with name '{0}'", Context.MainFunction))

            Else
                'Cast Object type to T And return the requested variable.
                Return DirectCast(Scope.GetVariable(Context.MainFunction), T)
            End If
        Catch ex As Exception

        End Try

        'Return the requested variable

        'End If
        'Catch ex As Exception

        'End Try
    End Function

    Public Sub New(ByVal File As IO.FileInfo)
        _BaseFile = File
        _References = New Dictionary(Of String, String)
        _Code = ""

        Dim scripInfo As New ScriptInformation(ScriptFile:=File.FullName)
        _ScriptInfo = scripInfo

        Dim xDoc As XDocument = XDocument.Load(File.FullName)
        Dim _r = From item In xDoc...<References>.<Reference> _
                 Select item.Value, item.@Name

        Dim Strlang = xDoc.<Script>.@Language
        Dim __language As Languages
        __language = [Enum].Parse(GetType(Languages), Strlang)
        _Language = __language

        Select Case __language
            Case Languages.IronPython
                ScriptEngine = IronPython.Hosting.Python.CreateEngine

            Case Languages.IronRuby
                'Dim manager As ScriptDomainManager = GetLanguageByName("ruby")


        End Select

        Scope = ScriptEngine.CreateScope

        For Each item In _r
            Dim _ref As String = item.Value
            If _ref.StartsWith("\") Then
                _ref = String.Format("{0}{1}", _BaseFile.Directory, item.Value)
            End If
            _References.Add(item.Name, _ref)
        Next

        _Code = xDoc.<Script>.<Code>.Value

    End Sub

    Public Sub New(ByVal File As IO.FileInfo, ByVal ScriptEngine As Object)
        _BaseFile = File
        _References = New Dictionary(Of String, String)
        _Code = ""
        Try
            ScriptEngine = ScriptEngine.Hosting.CreateEngine
        Catch ex As Exception
        End Try

        Scope = ScriptEngine.CreateScope

        Dim xDoc As XDocument = XDocument.Load(File.FullName)
        Dim _r = From item In xDoc...<References>.<Reference> _
                 Select item.Value, item.@Name

        For Each item In _r
            Dim _ref As String = item.Value
            If _ref.StartsWith("\") Then
                _ref = String.Format("{0}{1}", _BaseFile.Directory, item.Value)
            End If
            _References.Add(item.Name, _ref)
        Next

        _Code = xDoc.<Script>.<Code>.Value
    End Sub

    Public Sub New(ByVal Script As Uri, ByVal CacheDir As IO.DirectoryInfo)
        Dim tmpFN As String = Guid.NewGuid.ToString
        _InstanceID = tmpFN


        If CacheDir.Exists = False Then
            CacheDir.Create()
        End If
        If Script.IsFile Then
            Dim ScriptPack As New IO.FileInfo(Script.LocalPath)
            PackageFile = ScriptPack

            ScriptPack.Unpackage(CacheDir.FullName)
        Else
            Dim WebClient As New System.Net.WebClient

            AddHandler WebClient.DownloadFileCompleted, AddressOf ScriptDownloaded
            Dim tmpFile As New IO.FileInfo(String.Format("{0}\{0}.cin", CacheDir, tmpFN))
            PackageFile = tmpFile
            WebClient.DownloadFileAsync(Script, tmpFile.FullName)

        End If

    End Sub


    Private Sub ScriptDownloaded(ByVal sender As Object, ByVal e As System.Net.DownloadStringCompletedEventArgs)

        Dim __Script = PackageFile.Unpackage(PackageFile.Directory.FullName)
        _BaseFile = __Script._BaseFile
        _Code = __Script.Code
        _References = __Script.References
        _ScriptInfo = __Script.ScriptInformation
        Scope = __Script.Scope
        _Language = __Script.Language

    End Sub

    Public Sub New()
        _References = New Dictionary(Of String, String)
        _Code = ""
    End Sub

End Class

Public Class LostVariable
    Inherits System.Exception

    Public Sub New(ByVal Message As String)
        MyBase.New(Message)
    End Sub

End Class

